package com.sogoodsoft.core.dao;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.SQLQuery;
import org.hibernate.Session;
import org.hibernate.metadata.ClassMetadata;
import org.hibernate.type.CharacterType;
import org.hibernate.type.DateType;
import org.hibernate.type.IntegerType;
import org.hibernate.type.LongType;
import org.hibernate.type.ShortType;
import org.hibernate.type.StringType;
import org.hibernate.type.Type;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.SessionFactoryUtils;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.util.Assert;

import com.sogoodsoft.core.support.Page;


public class GenericDao<T, PK extends Serializable> extends HibernateDaoSupport implements EntityDao<T, PK> {

	protected final Log logger = LogFactory.getLog(getClass());

	protected Class<T> entityClass;
	protected String entityClassName;

	@SuppressWarnings("unchecked")
	public GenericDao() {
		// 通过范型反射，获取在子类中定义的entityClass.
		this.entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
		entityClassName = entityClass.getSimpleName();
	}

	/**
	 * 根据id获取对象
	 */
	@Override
	@SuppressWarnings("unchecked")
	public T load(final PK id) {
		logger.info("load " + entityClassName + " instance by id: " + id);
		return (T) getHibernateTemplate().load(entityClass, id);
	}

	/**
	 * 根据id获取对象
	 */
	@SuppressWarnings("unchecked")
	private T get(final PK id) {
		logger.info("get " + entityClassName + " instance by id: " + id);
		return (T) getHibernateTemplate().get(entityClass, id);
	}
	
	/**
	 * 
	 */
	public void clear() {
	    logger.info("clear session ");
	    getSession().clear();
	}

	/**
	 * 获取全部对象
	 */
	@Override
	@SuppressWarnings("unchecked")
	public List<T> find() {
		logger.info("load all " + entityClassName + " instance");
		return getHibernateTemplate().loadAll(entityClass);
	}

	/**
	 * 根据实体查询对象.
	 */
	@Override
	public List<T> find(final T entity) {
		return find(entity, null, true);
	}

	/**
	 * 根据实体查询对象,带排序参数.
	 */
	@Override
	public List<T> find(final T entity, final String orderBy, final boolean isAsc) {

		logger.info("find " + entityClassName + " instance by entity");

		StringBuilder queryString = new StringBuilder();
		queryString.append("select t from ").append(entityClassName).append(" t where 1 = 1");

		Assert.notNull(entityClass);
		// 取Hibernate实体对象元数据信息
		ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
		Assert.notNull(meta, "Class " + entityClass + " not define in hibernate session factory.");

		// 处理主键字段
		if (meta.hasIdentifierProperty()) {
			String propertyName = meta.getIdentifierPropertyName();
			Type propertyType = meta.getIdentifierType();
			Object propertyValue = null;
			try {
				propertyValue = PropertyUtils.getProperty(entity, propertyName);
			} catch (Exception e) {
			}

			// 尚不支持实体类型和集合类型属性的处理
			if (!propertyType.isEntityType() && !propertyType.isCollectionType()) {
				try {
					propertyValue = PropertyUtils.getProperty(entity, propertyName);
				} catch (Exception e) {
				}

				if (propertyValue != null && propertyValue.toString().length() > 0) {
					// 判断数值型
					if (propertyType instanceof LongType || propertyType instanceof IntegerType || propertyType instanceof ShortType)
						queryString.append(" and t.").append(propertyName).append(" = ").append(propertyValue.toString());

					// 判断字符型
					if (propertyType instanceof StringType || propertyType instanceof CharacterType)
						queryString.append(" and t.").append(propertyName).append(" like '%").append(propertyValue.toString()).append("%'");

					// 判断日期型
					if (propertyType instanceof DateType) {
						SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
						String dateString = dateFormat.format((Date) propertyValue);

						queryString.append(" and ").append("t." + propertyName).append(" '").append(dateString).append("'");
					}
					logger.info(propertyName + ": " + propertyValue.toString() + ": " + propertyType.toString());
				}
			}
		}

		// 处理非主键字段
		String[] propertyNames = meta.getPropertyNames();
		Type[] propertyTypes = meta.getPropertyTypes();

		for (int i = 0; i < propertyNames.length; i++) {
			String propertyName = propertyNames[i];
			Type propertyType = propertyTypes[i];
			Object propertyValue = null;

			// 尚不支持实体类型和集合类型属性的处理
			if (!propertyType.isEntityType() && !propertyType.isCollectionType()) {
				try {
					propertyValue = PropertyUtils.getProperty(entity, propertyName);
				} catch (Exception e) {
				}

				if (propertyValue != null && propertyValue.toString().length() > 0) {
					// 判断数值型
					if (propertyType instanceof LongType || propertyType instanceof IntegerType || propertyType instanceof ShortType)
						queryString.append(" and t.").append(propertyName).append(" = ").append(propertyValue.toString());

					// 判断字符型
					if (propertyType instanceof StringType || propertyType instanceof CharacterType)
						queryString.append(" and t.").append(propertyName).append(" like '%").append(propertyValue.toString()).append("%'");

					// 判断日期型
					if (propertyType instanceof DateType) {
						SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
						String dateString = dateFormat.format((Date) propertyValue);

						queryString.append(" and ").append("t." + propertyName).append(" '").append(dateString).append("'");
					}
					logger.info(propertyName + ": " + propertyValue.toString() + ": " + propertyType.toString());
				}
			}
		}

		// 处理排序字段
		if (orderBy != null) {
			queryString.append(" order by t.").append(orderBy).append(" ").append(isAsc ? "asc" : "desc");
			logger.info(orderBy + ": " + isAsc);
		}

		logger.info(queryString.toString());
		return find(queryString.toString());
	}
	
	@Override
	public boolean repeatValidation(final String entity, final String field, final String fieldValue,final String id){
		logger.info("find " + entityClassName + " repeatValidation entity");
		StringBuilder queryString = new StringBuilder();
		queryString.append("select t from ").append(entity).append(" t where 1 = 1");
		queryString.append(" and t.").append(field).append(" = '").append(fieldValue).append("'");
		if(StringUtils.isNotEmpty(id)){
			queryString.append(" and t.id <> '").append(id).append("'");
		}
		List<T> l = find(queryString.toString());
		if(l!=null && l.size()>0){
			return false;
		}
		return true;
	}

	/**
	 * 根据HQL查询
	 */
	@SuppressWarnings("unchecked")
	public List<T> find(final String queryString, final Object... values) {
		Assert.hasText(queryString);
		logger.info("find " + entityClassName + " instance by query: " + queryString);
		return getHibernateTemplate().find(queryString, values);
	}
	
	/**
	 * 根据标准SQL查询
	 */
	@SuppressWarnings("unchecked")
	public List<T> findBySQL(final String sql, final Object... values) {
		SQLQuery query = getSession().createSQLQuery(sql);
		return query.list();
	}

	/**
	 * 保存对象
	 */
	public void save(T entity) {
		Assert.notNull(entity);
		logger.info("save " + entityClassName + " instance");
		getHibernateTemplate().save(entity);
	}

	/**
	 * 更新对象
	 */
	@SuppressWarnings("unchecked")
	public T update(T entity) {
		Assert.notNull(entity);
		logger.info("update " + entityClassName + " instance");
		getSession().clear();
		T result = (T) getHibernateTemplate().merge(entity);
		return result;
	}
	
	/**
     * 保存对象
     */
    public void saveOrUpdate(T entity) {
        Assert.notNull(entity);
        logger.info("save " + entityClassName + " instance");
        getHibernateTemplate().saveOrUpdate(entity);
    }

	/**
	 * 批量更新
	 * 
	 * @param update
	 *            批量更新的语句,不能使用join,只能使用子查询
	 * @return 返回所受影响的记录条数(并不是所有情况都返回updateHql所影响的条数)
	 */
    @SuppressWarnings("unchecked")
    public int update(final String updateHql, final Object... values) {
		return (Integer) this.getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query queryObject = session.createQuery(updateHql);
				prepareQuery(queryObject);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						queryObject.setParameter(i, values[i]);
					}
				}
				return queryObject.executeUpdate();
			}
		});
	}

	/**
	 * 根据entity删除对象
	 */
	public void delete(final T entity) {
		Assert.notNull(entity);
		logger.info("delete " + entityClassName + " instance");
		getHibernateTemplate().delete(entity);
	}

	/**
	 * 根据id删除对象
	 */
	public void delete(final PK id) {
		Assert.notNull(id);
		logger.info("delete " + entityClassName + " instance by id: " + id);
		getHibernateTemplate().delete(get(id));
	}

	/**
	 * 批量删除
	 * 
	 * @param delete
	 *            批量删除的语句,不能使用join,只能使用子查询
	 * @return 返回所受影响的记录条数(并不是所有情况都返回deleteHql所影响的条数)
	 */
	@SuppressWarnings("unchecked")
	public int delete(final String deleteHql, final Object... values) {
		return (Integer) this.getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException {
				Query queryObject = session.createQuery(deleteHql);
				prepareQuery(queryObject);
				if (values != null) {
					for (int i = 0; i < values.length; i++) {
						queryObject.setParameter(i, values[i]);
					}
				}
				return queryObject.executeUpdate();
			}
		});
	}

	/*
	 * 如果需要回调HibernateCallback的时候需要调用此方法
	 */
	private void prepareQuery(Query queryObject) {
		if (this.getHibernateTemplate().isCacheQueries()) {
			queryObject.setCacheable(true);
			if (this.getHibernateTemplate().getQueryCacheRegion() != null) {
				queryObject.setCacheRegion(this.getHibernateTemplate().getQueryCacheRegion());
			}
		}
		if (this.getHibernateTemplate().getFetchSize() > 0) {
			queryObject.setFetchSize(this.getHibernateTemplate().getFetchSize());
		}
		if (this.getHibernateTemplate().getMaxResults() > 0) {
			queryObject.setMaxResults(this.getHibernateTemplate().getMaxResults());
		}
		SessionFactoryUtils.applyTransactionTimeout(queryObject, getSessionFactory());
	}

	/**
	 * 根据entity查询对象进行分页查询
	 */
	public Page findPaged(final T entity, final int pageNo, final int pageSize) {
		return findPaged(entity, pageNo, pageSize, false);
	}

	@Override
    public Page findPaged(T entity, int pageNo, int pageSize, String orderBy, boolean isAsc) {
        logger.info("find paged " + entityClassName + " instance by entity, page no: " + pageNo + ", page size: " + pageSize + ", order by: " + orderBy + ", isAsc: " + isAsc);

        StringBuilder queryString = new StringBuilder();
        queryString.append("select t from ").append(entityClassName).append(" t where 1 = 1");

        Assert.notNull(entityClass);
        // 取Hibernate实体对象元数据信息
        ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
        Assert.notNull(meta, "Class " + entityClass + " not define in hibernate session factory.");

        // 处理主键字段
        if (meta.hasIdentifierProperty()) {
            String propertyName = meta.getIdentifierPropertyName();
            Type propertyType = meta.getIdentifierType();
            Object propertyValue = null;
            try {
                propertyValue = PropertyUtils.getProperty(entity, propertyName);
            } catch (Exception e) {
            }

            // 尚不支持实体类型和集合类型属性的处理
            if (!propertyType.isEntityType() && !propertyType.isCollectionType()) {
                try {
                    propertyValue = PropertyUtils.getProperty(entity, propertyName);
                } catch (Exception e) {
                }

                if (propertyValue != null && propertyValue.toString().length() > 0) {
                    // 判断数值型
                    if (propertyType instanceof LongType || propertyType instanceof IntegerType || propertyType instanceof ShortType)
                        queryString.append(" and t.").append(propertyName).append(" = ").append(propertyValue.toString());

                    // 判断字符型
                    if (propertyType instanceof StringType || propertyType instanceof CharacterType)
                        queryString.append(" and t.").append(propertyName).append(" like '%").append(propertyValue.toString()).append("%'");

                    // 判断日期型
                    if (propertyType instanceof DateType) {
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        String dateString = dateFormat.format((Date) propertyValue);

                        queryString.append(" and ").append("t." + propertyName).append(" '").append(dateString).append("'");
                    }
                    logger.info(propertyName + ": " + propertyValue.toString() + ": " + propertyType.toString());
                }
            }
        }

        // 处理非主键字段
        String[] propertyNames = meta.getPropertyNames();
        Type[] propertyTypes = meta.getPropertyTypes();

        for (int i = 0; i < propertyNames.length; i++) {
            String propertyName = propertyNames[i];
            Type propertyType = propertyTypes[i];
            Object propertyValue = null;

            // 尚不支持实体类型和集合类型属性的处理
            if (!propertyType.isEntityType() && !propertyType.isCollectionType()) {
                try {
                    propertyValue = PropertyUtils.getProperty(entity, propertyName);
                } catch (Exception e) {
                }

                if (propertyValue != null && propertyValue.toString().length() > 0) {
                    // 判断数值型
                    if (propertyType instanceof LongType || propertyType instanceof IntegerType || propertyType instanceof ShortType)
                        queryString.append(" and t.").append(propertyName).append(" = ").append(propertyValue.toString());

                    // 判断字符型
                    if (propertyType instanceof StringType || propertyType instanceof CharacterType)
                        queryString.append(" and t.").append(propertyName).append(" like '%").append(propertyValue.toString()).append("%'");

                    // 判断日期型
                    if (propertyType instanceof DateType) {
                        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        String dateString = dateFormat.format((Date) propertyValue);

                        queryString.append(" and ").append("t." + propertyName).append(" '").append(dateString).append("'");
                    }
                    logger.info(propertyName + ": " + propertyValue.toString() + ": " + propertyType.toString());
                }
            }
        }
        
        //  组织排序字段
        if (StringUtils.isNotEmpty(orderBy)) {
            queryString.append(" order by " + orderBy);
            if (isAsc) {
                queryString.append(" asc");
            } else {
                queryString.append(" desc");
            }
        }
        logger.info(queryString.toString());

        return findPaged(queryString.toString(), pageNo, pageSize);
    }

    /**
	 * 根据entity查询对象进行分页查询 暂时只支持数值,字符,日期三种类型的处理转换
	 */
	public Page findPaged(final T entity, final int pageNo, final int pageSize, final boolean isEnabled) {
		logger.info("find paged " + entityClassName + " instance by entity, page no: " + pageNo + ", page size: " + pageSize);

		StringBuilder queryString = new StringBuilder();
		queryString.append("select t from ").append(entityClassName).append(" t where 1 = 1");

		Assert.notNull(entityClass);
		// 取Hibernate实体对象元数据信息
		ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
		Assert.notNull(meta, "Class " + entityClass + " not define in hibernate session factory.");

		// 处理主键字段
		if (meta.hasIdentifierProperty()) {
			String propertyName = meta.getIdentifierPropertyName();
			Type propertyType = meta.getIdentifierType();
			Object propertyValue = null;
			try {
				propertyValue = PropertyUtils.getProperty(entity, propertyName);
			} catch (Exception e) {
			}

			// 尚不支持实体类型和集合类型属性的处理
			if (!propertyType.isEntityType() && !propertyType.isCollectionType()) {
				try {
					propertyValue = PropertyUtils.getProperty(entity, propertyName);
				} catch (Exception e) {
				}

				if (propertyValue != null && propertyValue.toString().length() > 0) {
					// 判断数值型
					if (propertyType instanceof LongType || propertyType instanceof IntegerType || propertyType instanceof ShortType)
						queryString.append(" and t.").append(propertyName).append(" = ").append(propertyValue.toString());

					// 判断字符型
					if (propertyType instanceof StringType || propertyType instanceof CharacterType)
						queryString.append(" and t.").append(propertyName).append(" like '%").append(propertyValue.toString()).append("%'");

					// 判断日期型
					if (propertyType instanceof DateType) {
						SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
						String dateString = dateFormat.format((Date) propertyValue);

						queryString.append(" and ").append("t." + propertyName).append(" '").append(dateString).append("'");
					}
					logger.info(propertyName + ": " + propertyValue.toString() + ": " + propertyType.toString());
				}
			}
		}

		// 处理非主键字段
		String[] propertyNames = meta.getPropertyNames();
		Type[] propertyTypes = meta.getPropertyTypes();

		for (int i = 0; i < propertyNames.length; i++) {
			String propertyName = propertyNames[i];
			Type propertyType = propertyTypes[i];
			Object propertyValue = null;

			// 尚不支持实体类型和集合类型属性的处理
			if (!propertyType.isEntityType() && !propertyType.isCollectionType()) {
				try {
					propertyValue = PropertyUtils.getProperty(entity, propertyName);
				} catch (Exception e) {
				}

				if (propertyValue != null && propertyValue.toString().length() > 0) {
					// 判断数值型
					if (propertyType instanceof LongType || propertyType instanceof IntegerType || propertyType instanceof ShortType)
						queryString.append(" and t.").append(propertyName).append(" = ").append(propertyValue.toString());

					// 判断字符型
					if (propertyType instanceof StringType || propertyType instanceof CharacterType)
						queryString.append(" and t.").append(propertyName).append(" like '%").append(propertyValue.toString()).append("%'");

					// 判断日期型
					if (propertyType instanceof DateType) {
						SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
						String dateString = dateFormat.format((Date) propertyValue);

						queryString.append(" and ").append("t." + propertyName).append(" '").append(dateString).append("'");
					}
					logger.info(propertyName + ": " + propertyValue.toString() + ": " + propertyType.toString());
				}
			}
		}
		logger.info(queryString.toString());

		return findPaged(queryString.toString(), pageNo, pageSize);
	}

	/**
	 * 分页查询函数
	 */
	public Page findPaged(final String queryString, final int pageNo, final int pageSize, final Object... values) {
		Assert.hasText(queryString);
		Assert.isTrue(pageNo >= 1, "pageNo should start from 1");
		logger.info("find paged " + entityClassName + " instance by query: " + queryString + ", page no: " + pageNo + ", page size: " + pageSize);

		// Count查询
		String countQueryString = "select count(*) " + removeSelect(removeOrders(queryString));
		List countlist = getHibernateTemplate().find(countQueryString, values);
		int totalCount = ((Long) countlist.get(0)).intValue();

		if (totalCount < 1)
			return new Page();
		// 实际查询返回分页对象
		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		Query query = createQuery(queryString, values);
		List list = query.setFirstResult(startIndex).setMaxResults(pageSize).list();

		return new Page(pageNo, pageSize, totalCount, list);
	}
	
	

	/**
	 * 创建Query对象. 对于需要first,max,fetchsize,cache,cacheRegion等诸多设置的函数,可以在返回Query后自行设置. 留意可以连续设置,如下：
	 * 
	 * <pre>
	 * dao.createQuery(queryString).setMaxResult(100).setCacheable(true).list();
	 * </pre>
	 * 
	 * 调用方式如下：
	 * 
	 * <pre>
	 *   dao.createQuery(queryString)
	 *   dao.createQuery(queryString,arg0);
	 *   dao.createQuery(queryString,arg0,arg1);
	 *   dao.createQuery(queryString,new Object[arg0,arg1,arg2])
	 * </pre>
	 * 
	 * @param values
	 *            可变参数.
	 */
	private Query createQuery(final String queryString, final Object... values) {
		Assert.hasText(queryString);
		Query query = getSession().createQuery(queryString);
		for (int i = 0; i < values.length; i++) {
			query.setParameter(i, values[i]);
		}
		return query;
	}

	/**
	 * 获取对象的主键名,辅助函数.
	 */
	public String getIdName() {
		Assert.notNull(entityClass);
		ClassMetadata meta = getSessionFactory().getClassMetadata(entityClass);
		Assert.notNull(meta, "Class " + entityClass + " not define in hibernate session factory.");
		String idName = meta.getIdentifierPropertyName();
		Assert.hasText(idName, entityClass.getSimpleName() + " has no identifier property define.");
		return idName;
	}

	/**
	 * 去除HQL的select子句,未考虑union的情况,用于findPaged.
	 */
	public static String removeSelect(final String queryString) {
		Assert.hasText(queryString);
		int beginPos = queryString.toLowerCase().indexOf("from");
		Assert.isTrue(beginPos != -1, "queryString : " + queryString + " must has a keyword 'from'");
		return queryString.substring(beginPos);
	}

	/**
	 * 去除HQL的order by子句,用于findPaged.
	 */
	public static String removeOrders(final String queryString) {
		Assert.hasText(queryString);
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*", Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(queryString);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}

	/**
	 * 取得entity的class
	 */
	public Class<T> getEntityClass() {
		return entityClass;
	}

}
